home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / awt / EventQueue.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  15.7 KB  |  501 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)EventQueue.java    1.46 98/09/16
  3.  *
  4.  * Copyright 1996-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.awt;
  16.  
  17. import java.awt.event.InvocationEvent;
  18. import java.awt.event.KeyEvent;
  19. import java.awt.ActiveEvent;
  20. import java.util.EmptyStackException;
  21. import java.lang.reflect.InvocationTargetException;
  22. import sun.awt.MagicEvent;
  23.  
  24. /**
  25.  * EventQueue is a platform-independent class that queues events, both
  26.  * from the underlying peer classes and from trusted application classes.
  27.  * There is only one EventQueue for each AppContext.
  28.  *
  29.  * @version 1.46 09/16/98
  30.  * @author Thomas Ball
  31.  * @author Fred Ecks
  32.  */
  33. public class EventQueue {
  34.  
  35.     // From Thread.java
  36.     private static int threadInitNumber;
  37.     private static synchronized int nextThreadNum() {
  38.     return threadInitNumber++;
  39.     }
  40.  
  41.     /* The actual queue of events, implemented as a linked-list. */
  42.     private EventQueueItem queue;
  43.  
  44.     /* The tail of the list. Maintained so that non-Component source events
  45.        can be appended to the end of the list without O(n) traversal. */
  46.     private EventQueueItem queueTail;
  47.  
  48.     /* The last element in the list which is a priority event. Will be null
  49.        if the list is empty or there are no priority events in the queue.
  50.        Priority events are MagicEvents with the PRIORITY_EVENT bit set
  51.        (high priority). */
  52.     private EventQueueItem lastPriorityItem;
  53.  
  54.     /*
  55.      * The next EventQueue on the stack, or null if this EventQueue is
  56.      * on the top of the stack.  If nextQueue is non-null, requests to post
  57.      * an event are forwarded to nextQueue.
  58.      */
  59.     private EventQueue nextQueue;
  60.  
  61.     /*
  62.      * The previous EventQueue on the stack, or null if this is the
  63.      * "base" EventQueue.
  64.      */
  65.     private EventQueue previousQueue;
  66.  
  67.     private EventDispatchThread dispatchThread;
  68.  
  69.     /*
  70.      * Debugging flag -- set true and recompile to enable checking.
  71.      */
  72.     private final static boolean debug = false;
  73.  
  74.     public EventQueue() {
  75.         queue = queueTail = lastPriorityItem = null;
  76.         String name = "AWT-EventQueue-" + nextThreadNum();
  77.         dispatchThread = new EventDispatchThread(name, this);
  78.         dispatchThread.setPriority(Thread.NORM_PRIORITY + 1);
  79.         dispatchThread.start();
  80.     }
  81.  
  82.     /**
  83.      * Post a 1.1-style event to the EventQueue.  If there is an
  84.      * existing event on the queue with the same ID and event source,
  85.      * the source component's coalesceEvents method will be called.
  86.      *
  87.      * @param theEvent an instance of java.awt.AWTEvent, or a
  88.      * subclass of it.
  89.      */
  90.     public synchronized void postEvent(AWTEvent theEvent) {
  91.         if (theEvent instanceof MagicEvent) {
  92.             long flags = ((MagicEvent)theEvent).getFlags();
  93.         if ((flags & MagicEvent.PRIORITY_EVENT) != 0) {
  94.                 postEvent(theEvent, true);
  95.         return;
  96.         }
  97.     }
  98.     
  99.     postEvent(theEvent, false);
  100.     }
  101.  
  102.     /*
  103.      * Inserts an event in the queue, at the proper position. Handles
  104.      * event coalescing.
  105.      */
  106.     private void postEvent(AWTEvent theEvent, boolean priorityEvent) {
  107.  
  108.         if (nextQueue != null) {
  109.             // Forward event to top of EventQueue stack.
  110.             nextQueue.postEvent(theEvent);
  111.             return;
  112.         }
  113.  
  114.         Object source = theEvent.getSource();
  115.         EventQueueItem eqi = new EventQueueItem(theEvent);
  116.         if (queue == null) {
  117.             queue = queueTail = eqi;
  118.             if (priorityEvent) {
  119.                 lastPriorityItem = eqi;
  120.             }
  121.             notifyAll();
  122.         } else {
  123.             // For Component source events, traverse the entire list,
  124.             // trying to coalesce events
  125.             if (source instanceof Component) {
  126.                 EventQueueItem q = queue;
  127.                 for (;;) {
  128.                     if (q.id == eqi.id && q.source == source) {
  129.                         AWTEvent coalescedEvent = 
  130.                             ((Component)source).coalesceEvents(q.event, 
  131.                                                                theEvent);
  132.                         if (coalescedEvent != null) {
  133.                             q.event = coalescedEvent;
  134.                             return;
  135.                         }
  136.                     }
  137.                     if (q.next != null) {
  138.                         q = q.next;
  139.                     } else {
  140.                         break;
  141.                     }
  142.                 }
  143.             }
  144.             
  145.             // The event was not coalesced or has non-Component source.
  146.             // Insert it into the queue.
  147.             if (priorityEvent && lastPriorityItem == null) {
  148.             // Post at front of queue. Set lastPriorityItem to front of
  149.             // queue.
  150.                 eqi.next = queue;
  151.                 queue = lastPriorityItem = eqi;
  152.             } else if (priorityEvent && lastPriorityItem == queueTail) {
  153.             // Post at end of queue. Set lastPriorityItem to end of queue.
  154.             queueTail.next = eqi;
  155.         queueTail = lastPriorityItem = eqi;
  156.             } else if (priorityEvent) {
  157.             // Post after lastPriorityItem. Set lastPriorityItem to new
  158.             // item.
  159.                 eqi.next = lastPriorityItem.next;
  160.                 lastPriorityItem.next = eqi;
  161.                 lastPriorityItem = eqi;
  162.             } else {
  163.             // Post at end of queue. lastPriorityItem unchanged.
  164.                 queueTail.next = eqi;
  165.                 queueTail = eqi;
  166.             }
  167.         }
  168.     } // postEvent()
  169.  
  170.     /**
  171.      * Remove an event from the queue and return it.  This method will
  172.      * block until an event has been posted by another thread.
  173.      * @return the next AWTEvent
  174.      * @exception InterruptedException 
  175.      *            if another thread has interrupted this thread.
  176.      */
  177.     public synchronized AWTEvent getNextEvent() throws InterruptedException {
  178.         while (queue == null) {
  179.             wait();
  180.         }
  181.         EventQueueItem eqi = queue;
  182.         if (queue == lastPriorityItem) {
  183.             lastPriorityItem = null;
  184.         }
  185.         queue = queue.next;
  186.     if (queue == null) {
  187.         queueTail = null;
  188.     }
  189.         return eqi.event;
  190.     }
  191.  
  192.     /**
  193.      * Return the first event without removing it.
  194.      * @return the first event
  195.      */
  196.     public synchronized AWTEvent peekEvent() {
  197.         return (queue != null) ? queue.event : null;
  198.     }
  199.  
  200.     /**
  201.      * Return the first event with the specified id, if any.
  202.      * @param id the id of the type of event desired.
  203.      * @return the first event of the specified id
  204.      */
  205.     public synchronized AWTEvent peekEvent(int id) {
  206.         EventQueueItem q = queue;
  207.         for (; q != null; q = q.next) {
  208.             if (q.id == id) {
  209.                 return q.event;
  210.             }
  211.         }
  212.         return null;
  213.     }
  214.  
  215.     /**
  216.      * Dispatch an event. The manner in which the event is
  217.      * dispatched depends upon the type of the event and the
  218.      * type of the event's source
  219.      * object:
  220.      * <p> </p>
  221.      * <table border>
  222.      * <tr>
  223.      *     <th>Event Type</th>
  224.      *     <th>Source Type</th> 
  225.      *     <th>Dispatched To</th>
  226.      * </tr>
  227.      * <tr>
  228.      *     <td>ActiveEvent</td>
  229.      *     <td>Any</td>
  230.      *     <td>event.dispatch()</td>
  231.      * </tr>
  232.      * <tr>
  233.      *     <td>Other</td>
  234.      *     <td>Component</td>
  235.      *     <td>source.dispatchEvent(AWTEvent)</td>
  236.      * </tr>
  237.      * <tr>
  238.      *     <td>Other</td>
  239.      *     <td>MenuComponent</td>
  240.      *     <td>source.dispatchEvent(AWTEvent)</td>
  241.      * </tr>
  242.      * <tr>
  243.      *     <td>Other</td>
  244.      *     <td>Other</td>
  245.      *     <td>No action (ignored)</td>
  246.      * </tr>
  247.      * </table>
  248.      * <p> </p>
  249.      * @param theEvent an instance of java.awt.AWTEvent, or a
  250.      * subclass of it.
  251.      */
  252.     protected void dispatchEvent(AWTEvent event) {
  253.         Object src = event.getSource();
  254.         if (event instanceof ActiveEvent) {
  255.             // This could become the sole method of dispatching in time.
  256.             ((ActiveEvent)event).dispatch();
  257.         } else if (src instanceof Component) {
  258.             ((Component)src).dispatchEvent(event);
  259.         } else if (src instanceof MenuComponent) {
  260.             ((MenuComponent)src).dispatchEvent(event);
  261.         } else {
  262.             System.err.println("unable to dispatch event: " + event);
  263.         }
  264.     }
  265.  
  266.     /**
  267.      * Replace the existing EventQueue with the specified one.
  268.      * Any pending events are transferred to the new EventQueue
  269.      * for processing by it.
  270.      *
  271.      * @param an EventQueue (or subclass thereof) instance to be used.
  272.      * @see      java.awt.EventQueue#pop
  273.      */
  274.     public synchronized void push(EventQueue newEventQueue) {
  275.     if (debug) {
  276.         System.out.println("EventQueue.push(" + newEventQueue + ")");
  277.     }
  278.  
  279.         if (nextQueue != null) {
  280.             nextQueue.push(newEventQueue);
  281.             return;
  282.         }
  283.  
  284.         synchronized (newEventQueue) {
  285.         // Transfer all events forward to new EventQueue.
  286.         while (peekEvent() != null) {
  287.                 boolean priority = (lastPriorityItem != null);
  288.         try {
  289.             newEventQueue.postEvent(getNextEvent(), priority);
  290.         } catch (InterruptedException ie) {
  291.             if (debug) {
  292.             System.err.println("interrupted push:");
  293.             ie.printStackTrace(System.err);
  294.             }
  295.         }
  296.         }
  297.  
  298.         newEventQueue.previousQueue = this;
  299.         }
  300.     nextQueue = newEventQueue;
  301.     }
  302.  
  303.     /**
  304.      * Stop dispatching events using this EventQueue instance.
  305.      * Any pending events are transferred to the previous
  306.      * EventQueue for processing by it.  
  307.      *
  308.      * @exception if no previous push was made on this EventQueue.
  309.      * @see      java.awt.EventQueue#push
  310.      */
  311.     protected void pop() throws EmptyStackException {
  312.     if (debug) {
  313.         System.out.println("EventQueue.pop(" + this + ")");
  314.     }
  315.  
  316.     // To prevent deadlock, we lock on the previous EventQueue before
  317.     // this one.  This uses the same locking order as everything else
  318.     // in EventQueue.java, so deadlock isn't possible.
  319.     EventQueue prev = previousQueue;
  320.     synchronized ((prev != null) ? prev : this) {
  321.       synchronized(this) {
  322.             if (nextQueue != null) {
  323.                 nextQueue.pop();
  324.                 return;
  325.             }
  326.             if (previousQueue == null) {
  327.                 throw new EmptyStackException();
  328.             }
  329.  
  330.         // Transfer all events back to previous EventQueue.
  331.         previousQueue.nextQueue = null;
  332.         while (peekEvent() != null) {
  333.                 boolean priority = (lastPriorityItem != null);
  334.         try {
  335.             previousQueue.postEvent(getNextEvent(), priority);
  336.         } catch (InterruptedException ie) {
  337.             if (debug) {
  338.             System.err.println("interrupted pop:");
  339.             ie.printStackTrace(System.err);
  340.             }
  341.         }
  342.         }
  343.         previousQueue = null;
  344.           }
  345.         }
  346.  
  347.     dispatchThread.stopDispatching(); // Must be done outside synchronized
  348.                       // block to avoid possible deadlock
  349.     }
  350.  
  351.     /**
  352.      * Returns true if the calling thread is the current AWT EventQueue's
  353.      * dispatch thread.  Use this call the ensure that a given
  354.      * task is being executed (or not being) on the current AWT
  355.      * EventDispatchThread.
  356.      *
  357.      * @return true if running on the current AWT EventQueue's dispatch thread.
  358.      */
  359.     public static boolean isDispatchThread() {
  360.     EventQueue eq = Toolkit.getEventQueue();
  361.     EventQueue next = eq.nextQueue;
  362.     while (next != null) {
  363.         eq = next;
  364.         next = eq.nextQueue;
  365.     }
  366.     return (Thread.currentThread() == eq.dispatchThread);
  367.     }
  368.  
  369.     /*
  370.      * Get the EventDispatchThread for this EventQueue.
  371.      */
  372.     final EventDispatchThread getDispatchThread() {
  373.     return dispatchThread;
  374.     }
  375.  
  376.     /*
  377.      * Change the target of any pending KeyEvents because of a focus change.
  378.      */
  379.     final synchronized void changeKeyEventFocus(Object newSource) {
  380.         EventQueueItem q = queue;
  381.         for (; q != null; q = q.next) {
  382.             if (q.event instanceof KeyEvent) {
  383.                 q.event.setSource(newSource);
  384.             }
  385.         }
  386.     }
  387.  
  388.     /*
  389.      * Remove any pending events for the specified source object.
  390.      * This method is normally called by the source's removeNotify method.
  391.      */
  392.     final synchronized void removeSourceEvents(Object source) {
  393.         EventQueueItem entry = queue;
  394.         EventQueueItem prev = null;
  395.         while (entry != null) {
  396.             if (entry.source == source) {
  397.                 if (entry == lastPriorityItem) {
  398.                     lastPriorityItem = prev;
  399.                 }
  400.                 if (prev == null) {
  401.                     queue = entry.next;
  402.                 } else {
  403.                     prev.next = entry.next;
  404.                 }
  405.             } else {
  406.                 prev = entry;
  407.             }
  408.             entry = entry.next;
  409.         }
  410.     queueTail = prev;
  411.     }
  412.  
  413.     /*   
  414.      * Remove any pending events of specified id for the specified source.
  415.      */
  416.     synchronized void removeSourceEvents(Object source, int id) {
  417.         EventQueueItem entry = queue;
  418.         EventQueueItem prev = null;
  419.         while (entry != null) {
  420.             if ((entry.event.getSource().equals(source)) && (entry.id == id)) {
  421.                 if (prev == null) {
  422.                     queue = entry.next;
  423.                 } else {
  424.                     prev.next = entry.next;
  425.                 }
  426.             }    
  427.             prev = entry;
  428.             entry = entry.next;
  429.         }
  430.     }  
  431.  
  432.     /**
  433.      * Causes <i>runnable</i> to have its run() method called in the dispatch
  434.      * thread of the EventQueue.  This will happen after all pending events
  435.      * are processed.
  436.      *
  437.      * @param runnable  the Runnable whose run() method should be executed
  438.      *                  synchronously on the EventQueue
  439.      * @see             #invokeAndWait
  440.      * @since           JDK1.2
  441.      */
  442.     public static void invokeLater(Runnable runnable) {
  443.         Toolkit.getEventQueue().postEvent(
  444.             new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
  445.     }
  446.  
  447.     /**
  448.      * Causes <i>runnable</i> to have its run() method called in the dispatch
  449.      * thread of the EventQueue.  This will happen after all pending events
  450.      * are processed.  The call blocks until this has happened.  This method
  451.      * will throw an Error if called from the event dispatcher thread.
  452.      *
  453.      * @param runnable  the Runnable whose run() method should be executed
  454.      *                  synchronously on the EventQueue
  455.      * @exception       InterruptedException  if another thread has
  456.      *                  interrupted this thread
  457.      * @exception       InvocationTargetException  if an exception is thrown
  458.      *                  when running <i>runnable</i>
  459.      * @see             #invokeLater
  460.      * @since           JDK1.2
  461.      */
  462.     public static void invokeAndWait(Runnable runnable)
  463.              throws InterruptedException, InvocationTargetException {
  464.  
  465.         if (EventQueue.isDispatchThread()) {
  466.             throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
  467.         }
  468.  
  469.     class AWTInvocationLock {}
  470.         Object lock = new AWTInvocationLock();
  471.  
  472.         EventQueue queue = Toolkit.getEventQueue();
  473.         InvocationEvent event = 
  474.             new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
  475.                 true);
  476.  
  477.         synchronized (lock) {
  478.             Toolkit.getEventQueue().postEvent(event);
  479.             lock.wait();
  480.         }
  481.  
  482.         Exception eventException = event.getException();
  483.         if (eventException != null) {
  484.             throw new InvocationTargetException(eventException);
  485.         }
  486.     }
  487. }
  488.  
  489. class EventQueueItem {
  490.     AWTEvent event;
  491.     int      id;
  492.     Object   source;
  493.     EventQueueItem next;
  494.  
  495.     EventQueueItem(AWTEvent evt) {
  496.         event = evt;
  497.         id = evt.getID();
  498.         source = evt.getSource();
  499.     }
  500. }
  501.